/**
 * XSLT Templater - jQuery plugin for cached transforming XML with XSLT
 * <http://www.xslt-templater.com>
 * 
 * Copyright (c) 2010 Tsarev Oleg (<mailto:tsarev.oi@mail.ru>)
 * <http://live-scopes.blogspot.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */

/**
 * 
 * jQuery client-side XSLT Templater plugin.
 * 
 * @author <a href="mailto:tsarev.oi@mail.ru">Tsarev Oleg</a>
 * @version 0.0.1
 * 
 */

(function($) {
	
    $.fn.xslt = function() {
        return this;
    }
    
    // Boolean key: Firefox, Opera, Chrome, Safari OR InternetExplorer 
    var FOCS;
    
    if (window.DOMParser != undefined  && 
    	window.XSLTProcessor != undefined && 
    	window.XMLHttpRequest != undefined) {
    		
       var processor = new XSLTProcessor();
       
       if ($.isFunction(processor.transformDocument)) {
           support = window.XMLSerializer != undefined;
       } else {
           support = true;
       }
       
       if (support) {
           FOCS = true;
       }
    } else if (document.recalc) {
        FOCS = false;
    }
    
    if (FOCS != undefined) {
    	
    	// Caches
    	/*import src="XSLTLocalCache.js" */
		$.getXSLTLocalCache = function (opt) {
			return new XSLTLocalCache(opt);
		}
		
		
		/*import src="XSLTBaseCache.js" */
		var baseCache = new XSLTBaseCache();
		
		
		/*import src="DebugFunctions.js" */
		
    	
    	// XSLT Processor
		/*import src="XSLTTransformer.js" */
		var transformer = new XSLTransformer(FOCS);
		
		
		/*import src="TermFunctions.js" */
		
		
		// main context function
    	
    	var str = /^\s*</;
    	
    	$.fn.xslt = function(xml, xslt, callback, cache, caching) {
    		
	        var target = $(this);
	        var transformed = false;
	        
	        if (caching == null) {
	            caching = {
	            	"xml":false,
	            	"xsl":true
	            }
	        } else if (caching === true) {
	        	caching = {
	            	"xml":true,
	            	"xsl":true
		        }
	        }
	        	
	        var innerCache = baseCache;
	        var cacheXMLKey = '';
	        var cacheXSLKey = '';
	        
	        var xmlparameter,xslparameter;
	        
	        function addToLocalCache(cache,xmlparameter,xslparameter) {
	        	if (cache) {
	                if (!cache.checkXmlDoc()) {
	                    cache.setXmlDoc(getCachedRepresentation(xmlparameter));
	                }
	                if (!cache.checkXslDoc()) {
	                    cache.setXslDoc(getCachedRepresentation(xslparameter));
	                }
					cache.unlock();
				}
	    	}
	    	
	    	function addToBaseCache(cache,xmlparameter,xslparameter) {
				if (caching  && caching.xml) {
					if (cacheXMLKey!='') {
						var cacheXMLValue = getCachedRepresentation(xmlparameter);
						innerCache.setElement(cacheXMLKey,cacheXMLValue);
					}
				}
				if (caching  && caching.xsl) {
					if (cacheXSLKey!='') {
						var cacheXSLValue = getCachedRepresentation(xslparameter);
						innerCache.setElement(cacheXSLKey,cacheXSLValue);
					}
				}
	    	}
	    	
	    	function call() {
	    		if (callback) {
					setTimeout(function(){
						callback();
					},10);
				}
	    	}
	    	
	    	var change = function() {
	        	
            	if (xmlparameter.readyState == completeValue && xslparameter.readyState == completeValue && !transformed) {
					
	        		transformer.calculate(target,xmlparameter,xslparameter, function(){
	        			addToLocalCache(cache,xmlparameter,xslparameter);
	        			
						addToBaseCache(cache,xmlparameter,xslparameter);
						
						removeElements(xmlparameter,xslparameter);
						
						call();
						
	        		});
					transformed = true;

	        	}
	    	}
	        
	    	xmlparameter = initXML(change);
	    	xslparameter = initXSL(change);
	        
	        if (cache) {
				cache.lock();
				cache.reset();
			}

			if (caching && caching.xml) {
				if (!checkObjectType(xml)) {
					var value = innerCache.getElement(xml);
					if (value) {
						debug(xml + " - already cached! Element:"+target.attr("id"));
						xml = value;
					}
				}	
			}
			
			if (checkObjectType(xml)) {
				xmlparameter = setFromCache(xmlparameter,xml);
	        } else if (str.test(xml)) {
	        	xmlparameter = setFromString(xmlparameter,xml);
				if (cache) {
			       	cache.setXmlDoc(getXMLRepresentation(xmlparameter));
			    }
				if (caching && caching.xml) {
		           	cacheXMLKey = xml;
		        }
	    	} else {
	    		if (caching && caching.xml) {
        			cacheXMLKey = xml;
    			}
	    		xmlparameter = setFromFile(xmlparameter,xml,change);
	    	}
			
			if (caching && caching.xsl) {
				if (!checkObjectType(xslt)) {
					var value = innerCache.getElement(xslt);
					if (value) {
						debug(xslt + " - already cached! Element:"+target.attr("id"));
						xslt = value;
					}
				}	
			}
			
			if (checkObjectType(xslt)) {
				xslparameter = setFromCache(xslparameter,xslt);
				change();
		    } else if (str.test(xslt)) {
		    	xslparameter = setFromString(xslparameter,xslt);
				if (cache) {
		        	cache.setXslDoc(getXMLRepresentation(xslparameter));
		        }
				if (caching && caching.xsl) {
		           	cacheXSLKey = xslt;
		        }
				callReaction(change);
		    } else {
		    	if (caching && caching.xsl) {
			        cacheXSLKey = xslt;
			    }
		    	xslparameter = setFromFile(xslparameter,xslt,change);
		    }
		    
		    includeElements(xmlparameter,xslparameter);
	        
	        return this;
		}
	}

})(jQuery);
